# -*- coding: Latin-1 -*- """ PySourceColor: color Python source code """ """ PySourceColor.py ---------------------------------------------------------------------------- A python source to colorized html/css/xhtml converter. Hacked by M.E.Farmer Jr. 2004, 2005 Python license ---------------------------------------------------------------------------- - HTML markup does not create w3c valid html, but it works on every browser i've tried so far.(I.E.,Mozilla/Firefox,Opera,Konqueror,wxHTML). - CSS markup is w3c validated html 4.01 strict, but will not render correctly on all browsers. - XHTML markup is w3c validated xhtml 1.0 strict, like html 4.01, will not render correctly on all browsers. ---------------------------------------------------------------------------- Features: -Three types of markup: html (default) css/html 4.01 strict xhtml 1.0 strict -Can tokenize and colorize: 12 types of strings 2 comment types numbers operators brackets math operators class / name def / name decorator / name keywords arguments class/def/decorator linenumbers names text -Eight colorschemes built-in: null mono lite (default) dark dark2 idle viewcvs pythonwin -Header and footer set to '' for builtin header / footer. give path to a file containing the html you want added as header or footer. -Arbitrary text and html html markup converts all to raw (TEXT token) #@# for raw -> send raw text. #$# for span -> inline html and text. #%# for div -> block level html and text. -Linenumbers Supports all styles. New token is called LINENUMBER. Defaults to NAME if not defined. Style options -ALL markups support these text styles: b = bold i = italic u = underline -CSS and XHTML has limited support for borders: HTML markup functions will ignore these. Optional: Border color in RGB hex Defaults to the text forecolor. #rrggbb = border color Border size: l = thick m = medium t = thin Border type: - = dashed . = dotted s = solid d = double g = groove r = ridge n = inset o = outset You can specify multiple sides, they will all use the same style. Optional: Default is full border. v = bottom < = left > = right ^ = top NOTE: Specify the styles you want. The markups will ignore unsupported styles Also note not all browsers can show these options -All tokens default to NAME if not defined so the only absolutely critical ones to define are: NAME, ERRORTOKEN, PAGEBACKGROUND ---------------------------------------------------------------------------- Example usage:: # import import PySourceColor as psc psc.convert('c:/Python22/PySourceColor.py', colors=psc.idle, show=1) # from module import * from PySourceColor import * convert('c:/Python22/Lib', colors=lite, markup="css", header='#$#This is a simpe heading
') # How to use a custom colorscheme, and most of the 'features' from PySourceColor import * new = { ERRORTOKEN: ('bui','#FF8080',''), DECORATOR_NAME: ('s','#AACBBC',''), DECORATOR: ('n','#333333',''), NAME: ('t.','#000000','#FFFFFF'), TRIPLEDOUBLEQUOTE_R: ('tub','#000000','#FFFFFF'), TRIPLEDOUBLEQUOTE_U: ('-', '#CCAABB','#FFFAFF'), LINENUMBER: ('ib-','#ff66aa','#7733FF'),] TEXT: ('','#546634',''), PAGEBACKGROUND: '#FFFAAA', } if __name__ == '__main__': import sys convert(sys.argv[1], './xhtml.html', colors=new, markup='xhtml', show=1, linenumbers=1) convert(sys.argv[1], './html.html', colors=new, markup='html', show=1, linenumbers=1) """ __all__ = ['ERRORTOKEN','DECORATOR_NAME', 'DECORATOR', 'ARGS', 'EXTRASPACE', 'NAME', 'NUMBER', 'OPERATOR', 'COMMENT', 'MATH_OPERATOR', 'DOUBLECOMMENT', 'CLASS_NAME', 'DEF_NAME', 'KEYWORD', 'BRACKETS', 'SINGLEQUOTE','SINGLEQUOTE_R','SINGLEQUOTE_U','DOUBLEQUOTE', 'DOUBLEQUOTE_R', 'DOUBLEQUOTE_U', 'TRIPLESINGLEQUOTE', 'TEXT', 'TRIPLESINGLEQUOTE_R', 'TRIPLESINGLEQUOTE_U', 'TRIPLEDOUBLEQUOTE', 'TRIPLEDOUBLEQUOTE_R', 'TRIPLEDOUBLEQUOTE_U', 'PAGEBACKGROUND', 'LINENUMBER', 'CODESTART', 'CODEEND', 'PY', 'TOKEN_NAMES', 'CSSHOOK', 'null', 'mono', 'lite', 'dark','dark2', 'pythonwin','idle', 'viewcvs', 'Usage', 'cli', 'str2stdout', 'path2stdout', 'Parser', 'str2file', 'str2html', 'str2css', 'str2markup', 'path2file', 'path2html', 'convert', 'walkdir', 'defaultColors', 'showpage', 'pageconvert','tagreplace', 'MARKUPDICT'] __title__ = 'PySourceColor' __version__ = "2.1a" __date__ = '25 April 2005' __author__ = "M.E.Farmer Jr." __credits__ = '''This was originally based on a python recipe submitted by Jürgen Hermann to ASPN. Now based on the voices in my head. M.E.Farmer 2004, 2005 Python license ''' import os import sys import time import glob import getopt import keyword import token import tokenize import traceback try : import cStringIO as StringIO except: import StringIO # Do not edit NAME = token.NAME NUMBER = token.NUMBER COMMENT = tokenize.COMMENT OPERATOR = token.OP ERRORTOKEN = token.ERRORTOKEN ARGS = token.NT_OFFSET + 1 DOUBLECOMMENT = token.NT_OFFSET + 2 CLASS_NAME = token.NT_OFFSET + 3 DEF_NAME = token.NT_OFFSET + 4 KEYWORD = token.NT_OFFSET + 5 SINGLEQUOTE = token.NT_OFFSET + 6 SINGLEQUOTE_R = token.NT_OFFSET + 7 SINGLEQUOTE_U = token.NT_OFFSET + 8 DOUBLEQUOTE = token.NT_OFFSET + 9 DOUBLEQUOTE_R = token.NT_OFFSET + 10 DOUBLEQUOTE_U = token.NT_OFFSET + 11 TRIPLESINGLEQUOTE = token.NT_OFFSET + 12 TRIPLESINGLEQUOTE_R = token.NT_OFFSET + 13 TRIPLESINGLEQUOTE_U = token.NT_OFFSET + 14 TRIPLEDOUBLEQUOTE = token.NT_OFFSET + 15 TRIPLEDOUBLEQUOTE_R = token.NT_OFFSET + 16 TRIPLEDOUBLEQUOTE_U = token.NT_OFFSET + 17 PAGEBACKGROUND = token.NT_OFFSET + 18 DECORATOR = token.NT_OFFSET + 19 DECORATOR_NAME = token.NT_OFFSET + 20 BRACKETS = token.NT_OFFSET + 21 MATH_OPERATOR = token.NT_OFFSET + 22 LINENUMBER = token.NT_OFFSET + 23 TEXT = token.NT_OFFSET + 24 PY = token.NT_OFFSET + 25 CODESTART = token.NT_OFFSET + 26 CODEEND = token.NT_OFFSET + 27 CSSHOOK = token.NT_OFFSET + 28 EXTRASPACE = token.NT_OFFSET + 29 # markup classname lookup MARKUPDICT = { ERRORTOKEN: 'py_err', DECORATOR_NAME: 'py_decn', DECORATOR: 'py_dec', ARGS: 'py_args', NAME: 'py_name', NUMBER: 'py_num', OPERATOR: 'py_op', COMMENT: 'py_com', DOUBLECOMMENT: 'py_dcom', CLASS_NAME: 'py_clsn', DEF_NAME: 'py_defn', KEYWORD: 'py_key', SINGLEQUOTE: 'py_sq', SINGLEQUOTE_R: 'py_sqr', SINGLEQUOTE_U: 'py_squ', DOUBLEQUOTE: 'py_dq', DOUBLEQUOTE_R: 'py_dqr', DOUBLEQUOTE_U: 'py_dqu', TRIPLESINGLEQUOTE: 'py_tsq', TRIPLESINGLEQUOTE_R: 'py_tsqr', TRIPLESINGLEQUOTE_U: 'py_tsqu', TRIPLEDOUBLEQUOTE: 'py_tdq', TRIPLEDOUBLEQUOTE_R: 'py_tdqr', TRIPLEDOUBLEQUOTE_U: 'py_tdqu', BRACKETS: 'py_bra', MATH_OPERATOR: 'py_mop', LINENUMBER: 'py_lnum', TEXT: 'py_text', } # might help users that want to create custom schemes TOKEN_NAMES= { ERRORTOKEN:'ERRORTOKEN', DECORATOR_NAME:'DECORATOR_NAME', DECORATOR:'DECORATOR', ARGS:'ARGS', NAME:'NAME', NUMBER:'NUMBER', OPERATOR:'OPERATOR', COMMENT:'COMMENT', DOUBLECOMMENT:'DOUBLECOMMENT', CLASS_NAME:'CLASS_NAME', DEF_NAME:'DEF_NAME', KEYWORD:'KEYWORD', SINGLEQUOTE:'SINGLEQUOTE', SINGLEQUOTE_R:'SINGLEQUOTE_R', SINGLEQUOTE_U:'SINGLEQUOTE_U', DOUBLEQUOTE:'DOUBLEQUOTE', DOUBLEQUOTE_R:'DOUBLEQUOTE_R', DOUBLEQUOTE_U:'DOUBLEQUOTE_U', TRIPLESINGLEQUOTE:'TRIPLESINGLEQUOTE', TRIPLESINGLEQUOTE_R:'TRIPLESINGLEQUOTE_R', TRIPLESINGLEQUOTE_U:'TRIPLESINGLEQUOTE_U', TRIPLEDOUBLEQUOTE:'TRIPLEDOUBLEQUOTE', TRIPLEDOUBLEQUOTE_R:'TRIPLEDOUBLEQUOTE_R', TRIPLEDOUBLEQUOTE_U:'TRIPLEDOUBLEQUOTE_U', BRACKETS:'BRACKETS', MATH_OPERATOR:'MATH_OPERATOR', LINENUMBER:'LINENUMBER', TEXT:'TEXT', PAGEBACKGROUND:'PAGEBACKGROUND', } ###################################################################### # Edit colors and styles to taste # Create your own scheme, just copy one below , rename and edit. # Custom styles must at least define NAME, ERRORTOKEN, PAGEBACKGROUND, # all missing elements will default to NAME. # See module docstring for details on style attributes. ###################################################################### # Copy null and use it as a starter colorscheme. null = {# tokentype: ('tags border_color', 'textforecolor', 'textbackcolor') ERRORTOKEN: ('','#000000',''),# Error token DECORATOR_NAME: ('','#000000',''),# Decorator name DECORATOR: ('','#000000',''),# @ symbol ARGS: ('','#000000',''),# class,def,deco arguments NAME: ('','#000000',''),# All other python text NUMBER: ('','#000000',''),# 0->10 OPERATOR: ('','#000000',''),# ':','<=',';',',','.','==', etc MATH_OPERATOR: ('','#000000',''),# '+','-','=','','**',etc BRACKETS: ('','#000000',''),# '[',']','(',')','{','}' COMMENT: ('','#000000',''),# Single comment DOUBLECOMMENT: ('','#000000',''),## Double comment CLASS_NAME: ('','#000000',''),# Class name DEF_NAME: ('','#000000',''),# Def name KEYWORD: ('','#000000',''),# Python keywords SINGLEQUOTE: ('','#000000',''),# 'SINGLEQUOTE' SINGLEQUOTE_R: ('','#000000',''),# r'SINGLEQUOTE' SINGLEQUOTE_U: ('','#000000',''),# u'SINGLEQUOTE' DOUBLEQUOTE: ('','#000000',''),# "DOUBLEQUOTE" DOUBLEQUOTE_R: ('','#000000',''),# r"DOUBLEQUOTE" DOUBLEQUOTE_U: ('','#000000',''),# u"DOUBLEQUOTE" TRIPLESINGLEQUOTE: ('','#000000',''),# '''TRIPLESINGLEQUOTE''' TRIPLESINGLEQUOTE_R: ('','#000000',''),# r'''TRIPLESINGLEQUOTE''' TRIPLESINGLEQUOTE_U: ('','#000000',''),# u'''TRIPLESINGLEQUOTE''' TRIPLEDOUBLEQUOTE: ('','#000000',''),# """TRIPLEDOUBLEQUOTE""" TRIPLEDOUBLEQUOTE_R: ('','#000000',''),# r"""TRIPLEDOUBLEQUOTE""" TRIPLEDOUBLEQUOTE_U: ('','#000000',''),# u"""TRIPLEDOUBLEQUOTE""" TEXT: ('','#000000',''),# non python text LINENUMBER: ('>ti#555555','#000000',''),# Linenumbers PAGEBACKGROUND: '#FFFFFF'# set the page background } mono = { ERRORTOKEN: ('s#FF0000','#FF8080',''), DECORATOR_NAME: ('bu','#000000',''), DECORATOR: ('b','#000000',''), ARGS: ('b','#555555',''), NAME: ('','#000000',''), NUMBER: ('b','#000000',''), OPERATOR: ('b','#000000',''), MATH_OPERATOR: ('b','#000000',''), BRACKETS: ('b','#000000',''), COMMENT: ('i','#999999',''), DOUBLECOMMENT: ('b','#999999',''), CLASS_NAME: ('bu','#000000',''), DEF_NAME: ('b','#000000',''), KEYWORD: ('b','#000000',''), SINGLEQUOTE: ('','#000000',''), SINGLEQUOTE_R: ('','#000000',''), SINGLEQUOTE_U: ('','#000000',''), DOUBLEQUOTE: ('','#000000',''), DOUBLEQUOTE_R: ('','#000000',''), DOUBLEQUOTE_U: ('','#000000',''), TRIPLESINGLEQUOTE: ('','#000000',''), TRIPLESINGLEQUOTE_R: ('','#000000',''), TRIPLESINGLEQUOTE_U: ('','#000000',''), TRIPLEDOUBLEQUOTE: ('i','#000000',''), TRIPLEDOUBLEQUOTE_R: ('i','#000000',''), TRIPLEDOUBLEQUOTE_U: ('i','#000000',''), TEXT: ('','#000000',''), LINENUMBER: ('>ti#555555','#000000',''), PAGEBACKGROUND: '#FFFFFF' } dark = { ERRORTOKEN: ('s#FF0000','#FF8080',''), DECORATOR_NAME: ('b','#FFBBAA',''), DECORATOR: ('b','#CC5511',''), ARGS: ('b','#DDDDFF',''), NAME: ('','#DDDDDD',''), NUMBER: ('','#FF0000',''), OPERATOR: ('b','#FAF785',''), MATH_OPERATOR: ('b','#FAF785',''), BRACKETS: ('b','#FAF785',''), COMMENT: ('','#45FCA0',''), DOUBLECOMMENT: ('i','#A7C7A9',''), CLASS_NAME: ('b','#B666FD',''), DEF_NAME: ('b','#EBAE5C',''), KEYWORD: ('b','#8680FF',''), SINGLEQUOTE: ('','#F8BAFE',''), SINGLEQUOTE_R: ('','#F8BAFE',''), SINGLEQUOTE_U: ('','#F8BAFE',''), DOUBLEQUOTE: ('','#FF80C0',''), DOUBLEQUOTE_R: ('','#FF80C0',''), DOUBLEQUOTE_U: ('','#FF80C0',''), TRIPLESINGLEQUOTE: ('','#FF9595',''), TRIPLESINGLEQUOTE_R: ('','#FF9595',''), TRIPLESINGLEQUOTE_U: ('','#FF9595',''), TRIPLEDOUBLEQUOTE: ('','#B3FFFF',''), TRIPLEDOUBLEQUOTE_R: ('','#B3FFFF',''), TRIPLEDOUBLEQUOTE_U: ('','#B3FFFF',''), TEXT: ('','#FFFFFF',''), LINENUMBER: ('>mi#555555','#bbccbb','#333333'), PAGEBACKGROUND: '#000000' } dark2 = { ERRORTOKEN: ('','#FF0000',''), DECORATOR_NAME: ('b','#FFBBAA',''), DECORATOR: ('b','#CC5511',''), ARGS: ('b','#DDDDDD',''), NAME: ('','#C0C0C0',''), NUMBER: ('b','#00FF00',''), OPERATOR: ('b','#FF090F',''), MATH_OPERATOR: ('b','#EE7020',''), BRACKETS: ('b','#FFB90F',''), COMMENT: ('i','#D0D000','#522000'),#'#88AA88','#11111F'), DOUBLECOMMENT: ('i','#D0D000','#522000'),#'#77BB77','#11111F'), CLASS_NAME: ('b','#DD4080',''), DEF_NAME: ('b','#FF8040',''), KEYWORD: ('b','#4726d1',''), SINGLEQUOTE: ('','#8080C0',''), SINGLEQUOTE_R: ('','#8080C0',''), SINGLEQUOTE_U: ('','#8080C0',''), DOUBLEQUOTE: ('','#ADB9F1',''), DOUBLEQUOTE_R: ('','#ADB9F1',''), DOUBLEQUOTE_U: ('','#ADB9F1',''), TRIPLESINGLEQUOTE: ('','#00C1C1',''),#A050C0 TRIPLESINGLEQUOTE_R: ('','#00C1C1',''),#A050C0 TRIPLESINGLEQUOTE_U: ('','#00C1C1',''),#A050C0 TRIPLEDOUBLEQUOTE: ('','#33E3E3',''),#B090E0 TRIPLEDOUBLEQUOTE_R: ('','#33E3E3',''),#B090E0 TRIPLEDOUBLEQUOTE_U: ('','#33E3E3',''),#B090E0 TEXT: ('','#C0C0C0',''), LINENUMBER: ('>mi#555555','#bbccbb','#333333'), PAGEBACKGROUND: '#000000' } lite = { ERRORTOKEN: ('s#FF0000','#FF8080',''), DECORATOR_NAME: ('b','#BB4422',''), DECORATOR: ('b','#3333AF',''), ARGS: ('b','#000000',''), NAME: ('','#333333',''), NUMBER: ('b','#DD2200',''), OPERATOR: ('b','#000000',''), MATH_OPERATOR: ('b','#000000',''), BRACKETS: ('b','#000000',''), COMMENT: ('','#007F00',''), DOUBLECOMMENT: ('','#608060',''), CLASS_NAME: ('b','#0000DF',''), DEF_NAME: ('b','#9C7A00',''),#f09030 KEYWORD: ('b','#0000AF',''), SINGLEQUOTE: ('','#600080',''), SINGLEQUOTE_R: ('','#600080',''), SINGLEQUOTE_U: ('','#600080',''), DOUBLEQUOTE: ('','#A0008A',''), DOUBLEQUOTE_R: ('','#A0008A',''), DOUBLEQUOTE_U: ('','#A0008A',''), TRIPLESINGLEQUOTE: ('','#337799',''), TRIPLESINGLEQUOTE_R: ('','#337799',''), TRIPLESINGLEQUOTE_U: ('','#337799',''), TRIPLEDOUBLEQUOTE: ('','#1166AA',''), TRIPLEDOUBLEQUOTE_R: ('','#1166AA',''), TRIPLEDOUBLEQUOTE_U: ('','#1166AA',''), TEXT: ('','#000000',''), LINENUMBER: ('>ti#555555','#000000',''), PAGEBACKGROUND: '#FFFFFF' } idle = { ERRORTOKEN: ('s#FF0000','#FF8080',''), DECORATOR_NAME: ('','#900090',''), DECORATOR: ('','#FF7700',''), NAME: ('','#000000',''), NUMBER: ('','#000000',''), OPERATOR: ('','#000000',''), MATH_OPERATOR: ('','#000000',''), BRACKETS: ('','#000000',''), COMMENT: ('','#DD0000',''), DOUBLECOMMENT: ('','#DD0000',''), CLASS_NAME: ('','#0000FF',''), DEF_NAME: ('','#0000FF',''), KEYWORD: ('','#FF7700',''), SINGLEQUOTE: ('','#00AA00',''), SINGLEQUOTE_R: ('','#00AA00',''), SINGLEQUOTE_U: ('','#00AA00',''), DOUBLEQUOTE: ('','#00AA00',''), DOUBLEQUOTE_R: ('','#00AA00',''), DOUBLEQUOTE_U: ('','#00AA00',''), TRIPLESINGLEQUOTE: ('','#00AA00',''), TRIPLESINGLEQUOTE_R: ('','#00AA00',''), TRIPLESINGLEQUOTE_U: ('','#00AA00',''), TRIPLEDOUBLEQUOTE: ('','#00AA00',''), TRIPLEDOUBLEQUOTE_R: ('','#00AA00',''), TRIPLEDOUBLEQUOTE_U: ('','#00AA00',''), TEXT: ('','#000000',''), LINENUMBER: ('>ti#555555','#000000',''), PAGEBACKGROUND: '#FFFFFF' } pythonwin = { ERRORTOKEN: ('s#FF0000','#FF8080',''), DECORATOR_NAME: ('b','#DD0080',''), DECORATOR: ('b','#000080',''), ARGS: ('','#000000',''), NAME: ('','#303030',''), NUMBER: ('','#008080',''), OPERATOR: ('','#000000',''), MATH_OPERATOR: ('','#000000',''), BRACKETS: ('','#000000',''), COMMENT: ('','#007F00',''), DOUBLECOMMENT: ('','#7F7F7F',''), CLASS_NAME: ('b','#0000FF',''), DEF_NAME: ('b','#007F7F',''), KEYWORD: ('b','#000080',''), SINGLEQUOTE: ('','#808000',''), SINGLEQUOTE_R: ('','#808000',''), SINGLEQUOTE_U: ('','#808000',''), DOUBLEQUOTE: ('','#808000',''), DOUBLEQUOTE_R: ('','#808000',''), DOUBLEQUOTE_U: ('','#808000',''), TRIPLESINGLEQUOTE: ('','#808000',''), TRIPLESINGLEQUOTE_R: ('','#808000',''), TRIPLESINGLEQUOTE_U: ('','#808000',''), TRIPLEDOUBLEQUOTE: ('','#808000',''), TRIPLEDOUBLEQUOTE_R: ('','#808000',''), TRIPLEDOUBLEQUOTE_U: ('','#808000',''), TEXT: ('','#303030',''), LINENUMBER: ('>ti#555555','#000000',''), PAGEBACKGROUND: '#FFFFFF' } viewcvs = { ERRORTOKEN: ('s#FF0000','#FF8080',''), DECORATOR_NAME: ('','#000000',''), DECORATOR: ('','#000000',''), ARGS: ('','#000000',''), NAME: ('','#000000',''), NUMBER: ('','#000000',''), OPERATOR: ('','#000000',''), MATH_OPERATOR: ('','#000000',''), BRACKETS: ('','#000000',''), COMMENT: ('i','#b22222',''), DOUBLECOMMENT: ('i','#b22222',''), CLASS_NAME: ('','#000000',''), DEF_NAME: ('b','#0000ff',''), KEYWORD: ('b','#a020f0',''), SINGLEQUOTE: ('b','#bc8f8f',''), SINGLEQUOTE_R: ('b','#bc8f8f',''), SINGLEQUOTE_U: ('b','#bc8f8f',''), DOUBLEQUOTE: ('b','#bc8f8f',''), DOUBLEQUOTE_R: ('b','#bc8f8f',''), DOUBLEQUOTE_U: ('b','#bc8f8f',''), TRIPLESINGLEQUOTE: ('b','#bc8f8f',''), TRIPLESINGLEQUOTE_R: ('b','#bc8f8f',''), TRIPLESINGLEQUOTE_U: ('b','#bc8f8f',''), TRIPLEDOUBLEQUOTE: ('b','#bc8f8f',''), TRIPLEDOUBLEQUOTE_R: ('b','#bc8f8f',''), TRIPLEDOUBLEQUOTE_U: ('b','#bc8f8f',''), TEXT: ('','#000000',''), LINENUMBER: ('>ti#555555','#000000',''), PAGEBACKGROUND: '#FFFFFF' } defaultColors = lite def Usage(): doc = """ ----------------------------------------------------------------------------- PySourceColor.py ver: %s ----------------------------------------------------------------------------- Module summary: This module is designed to colorize python source code. Input--->python source Output-->colorized (html, html4.01/css, xhtml1.0) Standalone: This module will work from the command line with options. This module will work with redirected stdio. Imported: This module can be imported and used directly in your code. ----------------------------------------------------------------------------- Command line options: -h, --help Optional-> Display this help message. -t, --test Optional-> Will ignore all others flags but --profile test all schemes and markup combinations -p, --profile Optional-> Works only with --test or -t runs profile.py and makes the test work in quiet mode. -i, --in, --input Optional-> If you give input on stdin. Use any of these for the current dir (.,cwd) Input can be file or dir. Input from stdin use one of the following (-,stdin) If stdin is used as input stdout is output unless specified. -o, --out, --output Optional-> output dir for the colorized source. default: output dir is the input dir. To output html to stdout use one of the following (-,stdout) Stdout can be used without stdin if you give a file as input. -c, --color Optional-> null, mono, dark, dark2, lite, idle, pythonwin, viewcvs default: dark -s, --show Optional-> Show page after creation. default: no show -m, --markup Optional-> html, css, xhtml css, xhtml also support external stylesheets (-e,--external) default: HTML -e, --external Optional-> use with css, xhtml Writes an style sheet instead of embedding it in the page saves it as pystyle.css in the same directory. html markup will silently ignore this flag. -H, --header Opional-> add a page header to the top of the output -H Builtin header (name,date,hrule) --header You must specify a filename. The header file must be valid html and must handle its own font colors. ex. --header c:/tmp/header.txt -F, --footer Opional-> add a page footer to the bottom of the output -F Builtin footer (hrule,name,date) --footer You must specify a filename. The footer file must be valid html and must handle its own font colors. ex. --footer c:/tmp/footer.txt -l, --linenumbers Optional-> default is no linenumbers Adds line numbers to the start of each line in the code. --convertpage Given a webpage that has code embedded in tags it will convert embedded code to colorized html. (see pageconvert for details) ----------------------------------------------------------------------------- Option usage: # Test and show pages python PySourceColor.py -t -s # Test and only show profile results python PySourceColor.py -t -p # Colorize all .py,.pyw files in cwdir you can also use: (.,cwd) python PySourceColor.py -i . # Using long options w/ = python PySourceColor.py --in=c:/myDir/my.py --color=lite --show # Using short options w/out = python PySourceColor.py -i c:/myDir/ -c idle -m css -e # Using any mix python PySourceColor.py --in . -o=c:/myDir --show # Place a custom header on your files python PySourceColor.py -i . -o c:/tmp -m xhtml --header c:/header.txt ----------------------------------------------------------------------------- Stdio usage: # Stdio using no options python PySourceColor.py < c:/MyFile.py > c:/tmp/MyFile.html # Using stdin alone automatically uses stdout for output: (stdin,-) python PySourceColor.py -i- < c:/MyFile.py > c:/tmp/myfile.html # Stdout can also be written to directly from a file instead of stdin python PySourceColor.py -i c:/MyFile.py -m css -o- > c:/tmp/myfile.html # Stdin can be used as input , but output can still be specified python PySourceColor.py -i- -o c:/pydoc.py.html -s < c:/Python22/my.py _____________________________________________________________________________ """ print doc % (__version__) sys.exit(1) ###################################################### Command line interface def cli(): """Handle command line args and redirections""" try: # try to get command line args opts, args = getopt.getopt(sys.argv[1:], "hseqtplHFi:o:c:m:h:f:",["help", "show", "quiet", "test", "external", "linenumbers", "convertpage", "profile", "input=", "output=", "color=", "markup=","header=", "footer="]) except getopt.GetoptError: # on error print help information and exit: Usage() # init some names input = None output = None colorscheme = None markup = 'html' header = None footer = None linenumbers = 0 show = 0 quiet = 0 test = 0 profile = 0 convertpage = 0 form = None # if we have args then process them for o, a in opts: if o in ["-h", "--help"]: Usage() sys.exit() if o in ["-o", "--output", "--out"]: output = a if o in ["-i", "--input", "--in"]: input = a if input in [".", "cwd"]: input = os.getcwd() if o in ["-s", "--show"]: show = 1 if o in ["-q", "--quiet"]: quiet = 1 if o in ["-t", "--test"]: test = 1 if o in ["--convertpage"]: convertpage = 1 if o in ["-p", "--profile"]: profile = 1 if o in ["-e", "--external"]: form = 'external' if o in ["-m", "--markup"]: markup = str(a) if o in ["-l", "--linenumbers"]: linenumbers = 1 if o in ["--header"]: header = str(a) elif o == "-H": header = '' if o in ["--footer"]: footer = str(a) elif o == "-F": footer = '' if o in ["-c", "--color"]: try: colorscheme = globals().get(a.lower()) except: traceback.print_exc() Usage() if test: if profile: import profile profile.run('_test(show=%s, quiet=%s)'%(show,quiet)) else: # Parse this script in every possible colorscheme and markup _test(show,quiet) elif input in [None, "-", "stdin"] or output in ["-", "stdout"]: # determine if we are going to use stdio if input not in [None, "-", "stdin"]: if os.path.isfile(input) : path2stdout(input, colors=colorscheme, markup=markup, linenumbers=linenumbers, header=header, footer=footer, form=form) else: raise PathError, 'File does not exists!' else: try: if sys.stdin.isatty(): raise InputError, 'Please check input!' else: if output in [None,"-","stdout"]: str2stdout(sys.stdin.read(), colors=colorscheme, markup=markup, header=header, footer=footer, linenumbers=linenumbers, form=form) else: str2file(sys.stdin.read(), outfile=output, show=show, markup=markup, header=header, footer=footer, linenumbers=linenumbers, form=form) except: traceback.print_exc() Usage() else: if os.path.exists(input): if convertpage: # if there was at least an input given we can proceed pageconvert(input, out=output, colors=colorscheme, show=show, markup=markup,linenumbers=linenumbers) else: # if there was at least an input given we can proceed convert(source=input, outdir=output, colors=colorscheme, show=show, markup=markup, quiet=quiet, header=header, footer=footer, linenumbers=linenumbers, form=form) else: raise PathError, 'File does not exists!' Usage() ######################################################### Simple markup tests def _test(show=0, quiet=0): """Test the parser and most of the functions. There are 19 test total(eight colorschemes in three diffrent markups, and a str2file test. Most functions are tested by this. """ fi = sys.argv[0] if not fi.endswith('.exe'):# Do not test if frozen as an archive # this is a collection of test, most things are covered. path2file(fi, '/tmp/null.html', null, show=show, quiet=quiet) path2file(fi, '/tmp/null_css.html', null, show=show, markup='css', quiet=quiet) path2file(fi, '/tmp/mono.html', mono, show=show, quiet=quiet) path2file(fi, '/tmp/mono_css.html', mono, show=show, markup='css', quiet=quiet) path2file(fi, '/tmp/lite.html', lite, show=show, quiet=quiet) path2file(fi, '/tmp/lite_css.html', lite, show=show, markup='css', quiet=quiet, header='', footer='', linenumbers=1) path2file(fi, '/tmp/lite_xhtml.html', lite, show=show, markup='xhtml', quiet=quiet) path2file(fi, '/tmp/dark.html', dark, show=show, quiet=quiet) path2file(fi, '/tmp/dark_css.html', dark, show=show, markup='css', quiet=quiet, linenumbers=1) path2file(fi, '/tmp/dark2.html', dark2, show=show, quiet=quiet) path2file(fi, '/tmp/dark2_css.html', dark2, show=show, markup='css', quiet=quiet) path2file(fi, '/tmp/dark2_xhtml.html', dark2, show=show, markup='xhtml', quiet=quiet, header='', footer='', linenumbers=1, form='external') path2file(fi, '/tmp/idle.html', idle, show=show, quiet=quiet) path2file(fi, '/tmp/idle_css.html', idle, show=show, markup='css', quiet=quiet) path2file(fi, '/tmp/viewcvs.html', viewcvs, show=show, quiet=quiet, linenumbers=1) path2file(fi, '/tmp/viewcvs_css.html', viewcvs, show=show, markup='css', linenumbers=1, quiet=quiet) path2file(fi, '/tmp/pythonwin.html', pythonwin, show=show, quiet=quiet) path2file(fi, '/tmp/pythonwin_css.html', pythonwin, show=show, markup='css', quiet=quiet) teststr=r'''"""This is a test of decorators and other things""" # This should be line 421... @whatever(arg,arg2) @A @B(arghh) @C def LlamaSaysNi(arg='Ni!',arg2="RALPH"): """This docstring is deeply disturbed by all the llama references""" print '%s The Wonder Llama says %s'% (arg2,arg) # So I was like duh!, and he was like ya know?!, # and so we were both like huh...wtf!? RTFM!! LOL!!;) @staticmethod## Double comments are KewL. def LlamasRLumpy(): """This docstring is too sexy to be here. """ u""" ============================= A Møøse once bit my sister... ============================= """ ## Relax, this won't hurt a bit, just a simple, painless procedure, ## hold still while I get the anesthetizing hammer. m = {'three':'1','won':'2','too':'3'} o = r'fishy\fishy\fishy/fish\oh/where/is\my/little\..' python = uR""" No realli! She was Karving her initials øn the møøse with the sharpened end of an interspace tøøthbrush given her by Svenge - her brother-in-law -an Oslo dentist and star of many Norwegian møvies: "The Høt Hands of an Oslo Dentist", "Fillings of Passion", "The Huge Mølars of Horst Nordfink"...""" RU"""142 MEXICAN WHOOPING LLAMAS"""#<-Can you fit 142 llamas in a red box? n = u' HERMSGERVØRDENBRØTBØRDA ' + """ YUTTE """ t = """SAMALLNIATNUOMNAIRODAUCE"""+"DENIARTYLLAICEPS04" ## We apologise for the fault in the ## comments. Those responsible have been ## sacked. y = '14 NORTH CHILEAN GUANACOS \ (CLOSELY RELATED TO THE LLAMA)' rules = [0,1,2,3,4,5] print y''' htmlPath = os.path.abspath('/tmp/strtest_lines.html') str2file(teststr, htmlPath, colors=dark, markup='xhtml', linenumbers=420, show=show) _printinfo(" wrote %s" % htmlPath, quiet) htmlPath = os.path.abspath('/tmp/strtest_nolines.html') str2file(teststr, htmlPath, colors=dark, markup='xhtml', show=show) _printinfo(" wrote %s" % htmlPath, quiet) else: Usage() return # emacs wants this: ' ####################################################### User funtctions def str2stdout(sourcestring, colors=None, title='', markup='html', header=None, footer=None, linenumbers=0, form=None): """Converts a code(string) to colorized HTML. Writes to stdout. form='code',or'snip' (for "
yourcode
" only) colors=null,mono,lite,dark,dark2,idle,or pythonwin """ Parser(sourcestring, colors=colors, title=title, markup=markup, header=header, footer=footer, linenumbers=linenumbers).format(form) def path2stdout(sourcepath, title='', colors=None, markup='html', header=None, footer=None, linenumbers=0, form=None): """Converts code(file) to colorized HTML. Writes to stdout. form='code',or'snip' (for "
yourcode
" only) colors=null,mono,lite,dark,dark2,idle,or pythonwin """ sourcestring = open(sourcepath).read() Parser(sourcestring, colors=colors, title=sourcepath, markup=markup, header=header, footer=footer, linenumbers=linenumbers).format(form) def str2html(sourcestring, colors=None, title='', markup='html', header=None, footer=None, linenumbers=0, form=None): """Converts a code(string) to colorized HTML. Returns an HTML string. form='code',or'snip' (for "
yourcode
" only) colors=null,mono,lite,dark,dark2,idle,or pythonwin """ stringIO = StringIO.StringIO() Parser(sourcestring, colors=colors, title=title, out=stringIO, markup=markup, header=header, footer=footer, linenumbers=linenumbers).format(form) stringIO.seek(0) return stringIO.read() def str2css(sourcestring, colors=None, title='', markup='css', header=None, footer=None, linenumbers=0, form=None): """Converts a code string to colorized CSS/HTML. Returns CSS/HTML string If form != None then this will return (stylesheet_str, code_str) colors=null,mono,lite,dark,dark2,idle,or pythonwin """ if markup.lower() not in ['css' ,'xhtml']: markup = 'css' stringIO = StringIO.StringIO() parse = Parser(sourcestring, colors=colors, title=title, out=stringIO, markup=markup, header=header, footer=footer, linenumbers=linenumbers) parse.format(form) stringIO.seek(0) if form != None: return parse._sendCSSStyle(external=1), stringIO.read() else: return None, stringIO.read() def str2markup(sourcestring, colors=None, title = '', markup='xhtml', header=None, footer=None, linenumbers=0, form=None): """ Convert code strings into ([stylesheet or None], colorized string) """ if markup.lower() == 'html': return None, str2html(sourcestring, colors=colors, title=title, header=header, footer=footer, markup=markup, linenumbers=linenumbers, form=form) else: return str2css(sourcestring, colors=colors, title=title, header=header, footer=footer, markup=markup, linenumbers=linenumbers, form=form) def str2file(sourcestring, outfile, colors=None, title='', markup='html', header=None, footer=None, linenumbers=0, show=0, dosheet=1, form=None): """Converts a code string to a file. makes no attempt at correcting bad pathnames """ css , html = str2markup(sourcestring, colors=colors, title='', markup=markup, header=header, footer=footer, linenumbers=linenumbers, form=form) # write html f = open(outfile,'wt') f.writelines(html) f.close() #write css if css != None and dosheet: dir = os.path.dirname(outfile) outcss = os.path.join(dir,'pystyle.css') f = open(outcss,'wt') f.writelines(css) f.close() if show: showpage(outfile) def path2html(sourcepath, colors=None, markup='html', header=None, footer=None, linenumbers=0, form=None): """Converts code(file) to colorized HTML. Returns an HTML string. form='code',or'snip' (for "
yourcode
" only) colors=null,mono,lite,dark,dark2,idle,or pythonwin """ stringIO = StringIO.StringIO() sourcestring = open(sourcepath).read() Parser(sourcestring, colors, title=sourcepath, out=stringIO, markup=markup, header=header, footer=footer, linenumbers=linenumbers).format(form) stringIO.seek(0) return stringIO.read() def convert(source, outdir=None, colors=None, show=0, markup='html', quiet=0, header=None, footer=None, linenumbers=0, form=None): """Takes a file or dir as input and places the html in the outdir. If outdir is none it defaults to the input dir """ count=0 # If it is a filename then path2file if not os.path.isdir(source): if os.path.isfile(source): count+=1 path2file(source, outdir, colors, show, markup, quiet, form, header, footer, linenumbers, count) else: raise PathError, 'File does not exist!' # If we pass in a dir we need to walkdir for files. # Then we need to colorize them with path2file else: fileList = walkdir(source) if fileList != None: # make sure outdir is a dir if outdir != None: if os.path.splitext(outdir)[1] != '': outdir = os.path.split(outdir)[0] for item in fileList: count+=1 path2file(item, outdir, colors, show, markup, quiet, form, header, footer, linenumbers, count) _printinfo('Completed colorizing %s files.'%str(count), quiet) else: _printinfo("No files to convert in dir.", quiet) def path2file(sourcePath, out=None, colors=None, show=0, markup='html', quiet=0, form=None, header=None, footer=None, linenumbers=0, count=1): """ Converts python source to html file""" # If no outdir is given we use the sourcePath if out == None:#this is a guess htmlPath = sourcePath + '.html' else: # If we do give an out_dir, and it does # not exist , it will be created. if os.path.splitext(out)[1] == '': if not os.path.isdir(out): os.makedirs(out) sourceName = os.path.basename(sourcePath) htmlPath = os.path.join(out,sourceName)+'.html' # If we do give an out_name, and its dir does # not exist , it will be created. else: outdir = os.path.split(out)[0] if not os.path.isdir(outdir): os.makedirs(outdir) htmlPath = out htmlPath = os.path.abspath(htmlPath) # Open the text and do the parsing. source = open(sourcePath).read() parse = Parser(source, colors, sourcePath, open(htmlPath, 'wt'), markup, header, footer, linenumbers) parse.format(form) _printinfo(" wrote %s" % htmlPath, quiet) # html markup will ignore the external flag, but # we need to stop the blank file from being written. if form == 'external' and count == 1 and markup != 'html': cssSheet = parse._sendCSSStyle(external=1) cssPath = os.path.join(os.path.dirname(htmlPath),'pystyle.css') css = open(cssPath, 'wt') css.write(cssSheet) css.close() _printinfo(" wrote %s" % cssPath, quiet) if show: # load HTML page into the default web browser. showpage(htmlPath) return htmlPath def tagreplace(sourcestr, colors=lite, markup='xhtml', linenumbers=0, dosheet=1, tagstart=''.lower(), tagend=''.lower(), stylesheet='pystyle.css'): """This is a helper function for pageconvert. Returns css, page. """ if markup.lower() != 'html': link = '' css = link%stylesheet if sourcestr.find(css) == -1: sourcestr = sourcestr.replace('', css, 1) starttags = sourcestr.count(tagstart) endtags = sourcestr.count(tagend) if starttags: if starttags == endtags: for _ in range(starttags): datastart = sourcestr.find(tagstart) dataend = sourcestr.find(tagend) data = sourcestr[datastart+len(tagstart):dataend] data = unescape(data) css , data = str2markup(data, colors=colors, linenumbers=linenumbers, markup=markup, form='embed') start = sourcestr[:datastart] end = sourcestr[dataend+len(tagend):] sourcestr = ''.join([start,data,end]) else: raise InputError,'Tag mismatch!\nCheck %s,%s tags'%tagstart,tagend if not dosheet: css = None return css, sourcestr def pageconvert(path, out=None, colors=lite, markup='xhtml', linenumbers=0, dosheet=1, tagstart=''.lower(), tagend=''.lower(), stylesheet='pystyle', show=1, returnstr=0): """This function can colorize Python source that is written in a webpage enclosed in tags. """ if out == None: out = os.path.dirname(path) infile = open(path, 'r').read() css,page = tagreplace(sourcestr=infile,colors=colors, markup=markup, linenumbers=linenumbers, dosheet=dosheet, tagstart=tagstart, tagend=tagend, stylesheet=stylesheet) if not returnstr: newpath = os.path.abspath(os.path.join( out,'tmp', os.path.basename(path))) if not os.path.exists(newpath): try: os.makedirs(os.path.dirname(newpath)) except: pass#traceback.print_exc() #Usage() y = open(newpath, 'w') y.write(page) y.close() if css: csspath = os.path.abspath(os.path.join( out,'tmp','%s.css'%stylesheet)) x = open(csspath,'w') x.write(css) x.close() if show: try: os.startfile(newpath) except: traceback.print_exc() return newpath else: return css, page ##################################################################### helpers def walkdir(dir): """Return a list of .py and .pyw files from a given directory. This function can be written as a generator Python 2.3, or a genexp in Python 2.4. But 2.2 and 2.1 would be left out.... """ # Get a list of files that match *.py* GLOB_PATTERN = os.path.join(dir, "*.[p][y]*") pathlist = glob.glob(GLOB_PATTERN) # Now filter out all but py and pyw filterlist = [x for x in pathlist if x.endswith('.py') or x.endswith('.pyw')] if filterlist != []: # if we have a list send it return filterlist else: return None def showpage(path): """Helper function to open webpages""" try: import webbrowser webbrowser.open_new(os.path.abspath(path)) except: traceback.print_exc() def _printinfo(message, quiet): """Helper to print messages""" if not quiet: print message def escape(text): """escape text for html. similar to cgi.escape""" text = text.replace("&", "&") text = text.replace("<", "<") text = text.replace(">", ">") return text def unescape(text): """unsecape escaped text""" text = text.replace(""", '"') text = text.replace(">", ">") text = text.replace("<", "<") text = text.replace("&", "&") return text ########################################################### Custom Exceptions class PySourceColorError(Exception): # Base for custom errors def __init__(self, msg=''): self._msg = msg Exception.__init__(self, msg) def __repr__(self): return self._msg __str__ = __repr__ class PathError(PySourceColorError): def __init__(self, msg): PySourceColorError.__init__(self, 'Path error! : %s'% msg) class InputError(PySourceColorError): def __init__(self, msg): PySourceColorError.__init__(self, 'Input error! : %s'% msg) ########################################################## Python code parser class Parser(object): """MoinMoin python parser heavily chopped :)""" def __init__(self, raw, colors=None, title='', out=sys.stdout, markup='html', header=None, footer=None, linenumbers=0): """Store the source text & set some flags""" if colors == None: colors = defaultColors self.raw = raw.expandtabs().rstrip() self.title = os.path.basename(title) self.out = out self.line = '' self.lasttext = '' self.argFlag = 0 self.classFlag = 0 self.defFlag = 0 self.decoratorFlag = 0 self.external = 0 self.markup = markup.upper() self.colors = colors self.header = header self.footer = footer self.doArgs = 1 # overrides the new tokens self.doNames = 1 # overrides the new tokens self.doMathOps = 1 # overrides the new tokens self.doBrackets = 1 # overrides the new tokens self.doURL = 1 # override url conversion self.LINENUMHOLDER = "___line___".upper() self.LINESTART = "___start___".upper() self.skip = 0 # add space left side of code for padding.Override in color dict. self.extraspace = self.colors.get(EXTRASPACE, '') # Linenumbers less then zero also have numberlinks self.dolinenums = self.linenum = abs(linenumbers) if linenumbers < 0: self.numberlinks = 1 else: self.numberlinks = 0 def format(self, form=None): """Parse and send the colorized source""" if form in ('snip','code'): self.addEnds = 0 elif form == 'embed': self.addEnds = 0 self.external = 1 else: if form == 'external': self.external = 1 self.addEnds = 1 # Store line offsets in self.lines self.lines = [0, 0] pos = 0 # Add linenumbers if self.dolinenums: start=self.LINENUMHOLDER+' '+self.extraspace else: start=''+self.extraspace newlines = [] lines = self.raw.splitlines(0) for l in lines: # span and div escape for customizing and embedding raw text if (l.startswith('#$#') or l.startswith('#%#') or l.startswith('#@#')): newlines.append(l) else: # kludge for line spans in css,xhtml if self.markup in ['XHTML','CSS']: newlines.append(self.LINESTART+' '+start+l) else: newlines.append(start+l) self.raw = "\n".join(newlines)+'\n'# plus an extra newline at the end # Gather lines while 1: pos = self.raw.find('\n', pos) + 1 if not pos: break self.lines.append(pos) self.lines.append(len(self.raw)) # Wrap text in a filelike object self.pos = 0 text = StringIO.StringIO(self.raw) # Markup start if self.addEnds: self._doPageStart() else: self._doSnippetStart() ## Tokenize calls the __call__ ## function for each token till done. # Parse the source and write out the results. try: tokenize.tokenize(text.readline, self) except tokenize.TokenError, ex: msg = ex[0] line = ex[1][0] self.out.write("

ERROR: %s

%s\n"% (msg, self.raw[self.lines[line]:])) #traceback.print_exc() # Markup end if self.addEnds: self._doPageEnd() else: self._doSnippetEnd() def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line): """Token handler. Order is important do not rearrange.""" self.line = line # Calculate new positions oldpos = self.pos newpos = self.lines[srow] + scol self.pos = newpos + len(toktext) # Handle newlines if toktype in (token.NEWLINE, tokenize.NL): self.decoratorFlag = self.argFlag = 0 # kludge for line spans in css,xhtml if self.markup in ['XHTML','CSS']: self.out.write('') self.out.write('\n') return # Send the original whitespace, and tokenize backslashes if present. # Tokenizer.py just sends continued line backslashes with whitespace. # This is a hack to tokenize continued line slashes as operators. # Should continued line backslashes be treated as operators # or some other token? if newpos > oldpos: if self.raw[oldpos:newpos].isspace(): # consume a single space after linestarts and linenumbers # had to have them so tokenizer could seperate them. # multiline strings are handled by do_Text functions if self.lasttext != self.LINESTART \ and self.lasttext != self.LINENUMHOLDER: self.out.write(self.raw[oldpos:newpos]) else: self.out.write(self.raw[oldpos+1:newpos]) else: slash = self.raw[oldpos:newpos].find('\\')+oldpos self.out.write(self.raw[oldpos:slash]) getattr(self, '_send%sText'%(self.markup))(OPERATOR, '\\') self.linenum+=1 # kludge for line spans in css,xhtml if self.markup in ['XHTML','CSS']: self.out.write('') self.out.write(self.raw[slash+1:newpos]) # Skip indenting tokens if toktype in (token.INDENT, token.DEDENT): self.pos = newpos return # Look for operators if token.LPAR <= toktype and toktype <= token.OP: # Trap decorators py2.4 > if toktext == '@': toktype = DECORATOR # Set a flag if this was the decorator start so # the decorator name and arguments can be identified self.decoratorFlag = self.argFlag = 1 else: if self.doArgs: # Find the start for arguments if toktext == '(' and self.argFlag: self.argFlag = 2 # Find the end for arguments elif toktext == ':': self.argFlag = 0 ## Seperate the diffrent operator types # Brackets if self.doBrackets and toktext in ['[',']','(',')','{','}']: toktype = BRACKETS # Math operators elif self.doMathOps and toktext in ['*=','**=','-=','+=','|=', '%=','>>=','<<=','=','^=', '/=', '+','-','**','*','/','%']: toktype = MATH_OPERATOR # Operator else: toktype = OPERATOR # example how flags should work. # def fun(arg=argvalue,arg2=argvalue2): # 0 1 2 A 1 N 2 A 1 N 0 if toktext == "=" and self.argFlag == 2: self.argFlag = 1 elif toktext == "," and self.argFlag == 1: self.argFlag = 2 # Look for keywords elif toktype == NAME and keyword.iskeyword(toktext): toktype = KEYWORD # Set a flag if this was the class / def start so # the class / def name and arguments can be identified if toktext in ['class', 'def']: if toktext =='class' and \ not line[:line.find('class')].endswith('.'): self.classFlag = self.argFlag = 1 elif toktext == 'def' and \ not line[:line.find('def')].endswith('.'): self.defFlag = self.argFlag = 1 else: # must have used a keyword as a name i.e. self.class toktype = ERRORTOKEN # Look for class, def, decorator name elif (self.classFlag or self.defFlag or self.decoratorFlag) \ and self.doNames: if self.classFlag: self.classFlag = 0 toktype = CLASS_NAME elif self.defFlag: self.defFlag = 0 toktype = DEF_NAME elif self.decoratorFlag: self.decoratorFlag = 0 toktype = DECORATOR_NAME # Look for strings # Order of evaluation is important do not change. elif toktype == token.STRING: text = toktext.lower() # TRIPLE DOUBLE QUOTE's if (text[:3] == '"""'): toktype = TRIPLEDOUBLEQUOTE elif (text[:4] == 'r"""'): toktype = TRIPLEDOUBLEQUOTE_R elif (text[:4] == 'u"""' or text[:5] == 'ur"""'): toktype = TRIPLEDOUBLEQUOTE_U # DOUBLE QUOTE's elif (text[:1] == '"'): toktype = DOUBLEQUOTE elif (text[:2] == 'r"'): toktype = DOUBLEQUOTE_R elif (text[:2] == 'u"' or text[:3] == 'ur"'): toktype = DOUBLEQUOTE_U # TRIPLE SINGLE QUOTE's elif (text[:3] == "'''"): toktype = TRIPLESINGLEQUOTE elif (text[:4] == "r'''"): toktype = TRIPLESINGLEQUOTE_R elif (text[:4] == "u'''" or text[:5] == "ur'''"): toktype = TRIPLESINGLEQUOTE_U # SINGLE QUOTE's elif (text[:1] == "'"): toktype = SINGLEQUOTE elif (text[:2] == "r'"): toktype = SINGLEQUOTE_R elif (text[:2] == "u'" or text[:3] == "ur'"): toktype = SINGLEQUOTE_U # test for invalid string declaration if self.lasttext.lower() == 'ru': toktype = ERRORTOKEN # Look for comments elif toktype == COMMENT: if toktext[:2] == "##": toktype = DOUBLECOMMENT elif toktext[:3] == '#$#': toktype = TEXT self.textFlag = 'SPAN' toktext = toktext[3:] elif toktext[:3] == '#%#': toktype = TEXT self.textFlag = 'DIV' toktext = toktext[3:] elif toktext[:3] == '#@#': toktype = TEXT self.textFlag = 'RAW' toktext = toktext[3:] if self.doURL: # this is a 'fake helper function' # url(URI,Alias_name) or url(URI) url_pos = toktext.find('url(') if url_pos != -1: before = toktext[:url_pos] url = toktext[url_pos+4:] splitpoint = url.find(',') endpoint = url.find(')') after = url[endpoint+1:] url = url[:endpoint] if splitpoint != -1: urlparts = url.split(',',1) toktext = '%s%s%s'%( before,urlparts[0],urlparts[1].lstrip(),after) else: toktext = '%s%s%s'%(before,url,url,after) # Seperate errors from decorators elif toktype == ERRORTOKEN: # Bug fix for < py2.4 # space between decorators if self.argFlag and toktext.isspace(): #toktype = NAME self.out.write(toktext) return # Bug fix for py2.2 linenumbers with decorators elif toktext.isspace(): # What if we have a decorator after a >>> or ... #p = line.find('@') #if p >= 0 and not line[:p].isspace(): #self.out.write(toktext) #return if self.skip: self.skip=0 return else: self.out.write(toktext) return # trap decorators < py2.4 elif toktext == '@': toktype = DECORATOR # Set a flag if this was the decorator start so # the decorator name and arguments can be identified self.decoratorFlag = self.argFlag = 1 # Seperate args from names elif (self.argFlag == 2 and toktype == NAME and toktext != 'None' and self.doArgs): toktype = ARGS # Look for line numbers # The conversion code for them is in the send_text functions. if toktext in [self.LINENUMHOLDER,self.LINESTART]: toktype = LINENUMBER # if we don't have linenumbers set flag # to skip the trailing space from linestart if toktext == self.LINESTART and not self.dolinenums \ or toktext == self.LINENUMHOLDER: self.skip=1 # Skip blank token that made it thru ## bugfix for the last empty tag. if toktext == '': return # Last token text history self.lasttext = toktext # escape all but the urls in the comments if toktype in (DOUBLECOMMENT, COMMENT): if toktext.find('\n') else: # Start of css/xhtml snippet self.out.write(self.colors.get(CODESTART,'
\n'))

    def _doSnippetEnd(self):
        # End of html snippet
        self.out.write(self.colors.get(CODEEND,'
\n')) ######################################################## markup selectors def _getFile(self, filepath): try: _file = open(filepath,'r') content = _file.read() _file.close() except: traceback.print_exc() content = '' return content def _doPageStart(self): getattr(self, '_do%sStart'%(self.markup))() def _doPageHeader(self): if self.header != None: if self.header.find('#$#') != -1 or \ self.header.find('#$#') != -1 or \ self.header.find('#%#') != -1: self.out.write(self.header[3:]) else: if self.header != '': self.header = self._getFile(self.header) getattr(self, '_do%sHeader'%(self.markup))() def _doPageFooter(self): if self.footer != None: if self.footer.find('#$#') != -1 or \ self.footer.find('#@#') != -1 or \ self.footer.find('#%#') != -1: self.out.write(self.footer[3:]) else: if self.footer != '': self.footer = self._getFile(self.footer) getattr(self, '_do%sFooter'%(self.markup))() def _doPageEnd(self): getattr(self, '_do%sEnd'%(self.markup))() ################################################### color/style retrieval ## Some of these are not used anymore but are kept for documentation def _getLineNumber(self): num = self.linenum self.linenum+=1 return str(num).rjust(5)+" " def _getTags(self, key): # style tags return self.colors.get(key, self.colors[NAME])[0] def _getForeColor(self, key): # get text foreground color, if not set to black color = self.colors.get(key, self.colors[NAME])[1] if color[:1] != '#': color = '#000000' return color def _getBackColor(self, key): # get text background color return self.colors.get(key, self.colors[NAME])[2] def _getPageColor(self): # get page background color return self.colors.get(PAGEBACKGROUND, '#FFFFFF') def _getStyle(self, key): # get the token style from the color dictionary return self.colors.get(key, self.colors[NAME]) def _getMarkupClass(self, key): # get the markup class name from the markup dictionary return MARKUPDICT.get(key, MARKUPDICT[NAME]) def _getDocumentCreatedBy(self): return '\n'%( __title__,__version__,time.ctime()) ################################################### HTML markup functions def _doHTMLStart(self): # Start of html page self.out.write('\n') self.out.write('%s\n'%(self.title)) self.out.write(self._getDocumentCreatedBy()) self.out.write('\n') # Get background self.out.write('\n'%self._getPageColor()) self._doPageHeader() self.out.write('
')

    def _getHTMLStyles(self, toktype, toktext):
        # Get styles
        tags, color = self.colors.get(toktype, self.colors[NAME])[:2]#
        tagstart=[]
        tagend=[]
        # check for styles and set them if needed.
        if 'b' in tags:#Bold
            tagstart.append('')
            tagend.append('')
        if 'i' in tags:#Italics
            tagstart.append('')
            tagend.append('')
        if 'u' in tags:#Underline
            tagstart.append('')
            tagend.append('')
        # HTML tags should be paired like so : Doh!
        tagend.reverse()
        starttags="".join(tagstart)
        endtags="".join(tagend)
        return starttags,endtags,color

    def _sendHTMLText(self, toktype, toktext):
        numberlinks = self.numberlinks
        
        # If it is an error, set a red box around the bad tokens
        # older browsers should ignore it
        if toktype == ERRORTOKEN:
            style = ' style="border: solid 1.5pt #FF0000;"'
        else:
            style = ''
        # Get styles
        starttag, endtag, color = self._getHTMLStyles(toktype, toktext)
        # This is a hack to 'fix' multi-line  strings.
        # Multi-line strings are treated as only one token 
        # even though they can be several physical lines.
        # That makes it hard to spot the start of a line,
        # because at this level all we know about are tokens.
        
        if toktext.count(self.LINENUMHOLDER):
            # rip apart the string and separate it by line.
            # count lines and change all linenum token to line numbers.
            # embedded all the new font tags inside the current one.
            # Do this by ending the tag first then writing our new tags,
            # then starting another font tag exactly like the first one.
            if toktype == LINENUMBER:
                splittext = toktext.split(self.LINENUMHOLDER)
            else:    
                splittext = toktext.split(self.LINENUMHOLDER+' ')
            store = []
            store.append(splittext.pop(0))
            lstarttag, lendtag, lcolor = self._getHTMLStyles(LINENUMBER, toktext)
            count = len(splittext)
            for item in splittext:
                num =  self._getLineNumber()
                if numberlinks:
                    numstrip = num.strip()
                    content = '%s' \
                              %(numstrip,numstrip,num)
                else:
                    content = num
                if count <= 1:
                    endtag,starttag = '',''
                linenumber = ''.join([endtag,'',
                            lstarttag, content, lendtag, '' ,starttag])
                store.append(linenumber+item)
            toktext = ''.join(store)
        # send text
        ## Output optimization
        # skip font tag if black text, but styles will still be sent. (b,u,i)
        if color !='#000000':
            startfont = ''%(color, style)
            endfont = ''
        else:
            startfont, endfont = ('','')
        if toktype != LINENUMBER:
            self.out.write(''.join([startfont,starttag,
                                     toktext,endtag,endfont]))
        else:
            self.out.write(toktext)
        return

    def _doHTMLHeader(self):
        # Optional
        if self.header != '':
            self.out.write('%s\n'%self.header)
        else:
            color = self._getForeColor(NAME)
            self.out.write('# %s \
                            
# %s

\n'% (color, self.title, time.ctime())) def _doHTMLFooter(self): # Optional if self.footer != '': self.out.write('%s\n'%self.footer) else: color = self._getForeColor(NAME) self.out.write(' \
# %s
# %s
\n'% (color, self.title, time.ctime())) def _doHTMLEnd(self): # End of html page self.out.write('
\n') # Write a little info at the bottom self._doPageFooter() self.out.write('\n') #################################################### CSS markup functions def _getCSSStyle(self, key): # Get the tags and colors from the dictionary tags, forecolor, backcolor = self._getStyle(key) style=[] border = None bordercolor = None tags = tags.lower() if tags: # get the border color if specified # the border color will be appended to # the list after we define a border if '#' in tags:# border color start = tags.find('#') end = start + 7 bordercolor = tags[start:end] tags.replace(bordercolor,'',1) # text styles if 'b' in tags:# Bold style.append('font-weight:bold;') else: style.append('font-weight:normal;') if 'i' in tags:# Italic style.append('font-style:italic;') if 'u' in tags:# Underline style.append('text-decoration:underline;') # border size if 'l' in tags:# thick border size='thick' elif 'm' in tags:# medium border size='medium' elif 't' in tags:# thin border size='thin' else:# default size='medium' # border styles if 'n' in tags:# inset border border='inset' elif 'o' in tags:# outset border border='outset' elif 'r' in tags:# ridge border border='ridge' elif 'g' in tags:# groove border border='groove' elif '=' in tags:# double border border='double' elif '.' in tags:# dotted border border='dotted' elif '-' in tags:# dashed border border='dashed' elif 's' in tags:# solid border border='solid' # border type check seperate_sides=0 for side in ['<','>','^','v']: if side in tags: seperate_sides+=1 # border box or seperate sides if seperate_sides==0 and border: style.append('border: %s %s;'%(border,size)) else: if border == None: border = 'solid' if 'v' in tags:# bottom border style.append('border-bottom:%s %s;'%(border,size)) if '<' in tags:# left border style.append('border-left:%s %s;'%(border,size)) if '>' in tags:# right border style.append('border-right:%s %s;'%(border,size)) if '^' in tags:# top border style.append('border-top:%s %s;'%(border,size)) else: style.append('font-weight:normal;')# css inherited style fix # we have to define our borders before we set colors if bordercolor: style.append('border-color:%s;'%bordercolor) # text forecolor style.append('color:%s;'% forecolor) # text backcolor if backcolor: style.append('background-color:%s;'%backcolor) return (self._getMarkupClass(key),' '.join(style)) def _sendCSSStyle(self, external=0): """ create external and internal style sheets""" styles = [] external += self.external if not external: styles.append('\n') return ''.join(styles) def _doCSSStart(self): # Start of css/html 4.01 page self.out.write('\n') self.out.write('%s\n'%(self.title)) self.out.write(self._getDocumentCreatedBy()) self.out.write('\n') self._doCSSStyleSheet() self.out.write('\n\n') # Write a little info at the top. self._doPageHeader() self.out.write(self.colors.get(CODESTART,'
\n'))
        return

    def _doCSSStyleSheet(self):
        if not self.external:
            # write an embedded style sheet
            self.out.write(self._sendCSSStyle())
        else:
            # write a link to an external style sheet
            self.out.write('')
        return

    def _sendCSSText(self, toktype, toktext):
        # This is a hack to 'fix' multi-line strings.
        # Multi-line strings are treated as only one token 
        # even though they can be several physical lines.
        # That makes it hard to spot the start of a line,
        # because at this level all we know about are tokens.
        markupclass = MARKUPDICT.get(toktype, MARKUPDICT[NAME])
        # if it is a LINENUMBER type then we can skip the rest
        if toktext == self.LINESTART and toktype == LINENUMBER:
            self.out.write('')
            return
        if toktext.count(self.LINENUMHOLDER):
            # rip apart the string and separate it by line
            # count lines and change all linenum token to line numbers
            # also convert linestart and lineend tokens
            #   lnum  text 
            #################################################
            newmarkup = MARKUPDICT.get(LINENUMBER, MARKUPDICT[NAME])
            lstartspan = ''%(newmarkup)
            if toktype == LINENUMBER:
                splittext = toktext.split(self.LINENUMHOLDER)
            else:    
                splittext = toktext.split(self.LINENUMHOLDER+' ')
            store = []
            # we have already seen the first linenumber token
            # so we can skip the first one
            store.append(splittext.pop(0))
            for item in splittext:
                num = self._getLineNumber()
                if self.numberlinks:
                    numstrip = num.strip()
                    content= '%s' \
                              %(numstrip,numstrip,num)
                else:
                    content = num
                linenumber= ''.join([lstartspan,content,''])
                store.append(linenumber+item)
            toktext = ''.join(store)
        if toktext.count(self.LINESTART):
            # wraps the textline in a line span
            # this adds a lot of kludges, is it really worth it?
            store = []
            parts = toktext.split(self.LINESTART+' ')
            # handle the first part differently
            # the whole token gets wraqpped in a span later on
            first = parts.pop(0)
            # place spans before the newline
            pos = first.rfind('\n')
            if pos != -1:
                first=first[:pos]+''+first[pos:]
            store.append(first)
            #process the rest of the string
            for item in parts:
                #handle line numbers if present
                if self.dolinenums:
                    item = item.replace('',
                           ''%(markupclass))
                else:
                    item = '%s'%(markupclass,item)
                # add endings for line and string tokens
                pos = item.rfind('\n')
                if pos != -1:
                    item=item[:pos]+'\n'
                store.append(item)
            # add start tags for lines
            toktext = ''.join(store)
        # Send text
        if toktype != LINENUMBER:
            if toktype == TEXT and self.textFlag == 'DIV':
                startspan = '
'%(markupclass) endspan = '
' elif toktype == TEXT and self.textFlag == 'RAW': startspan,endspan = ('','') else: startspan = ''%(markupclass) endspan = '' self.out.write(''.join([startspan, toktext, endspan])) else: self.out.write(toktext) return def _doCSSHeader(self): if self.header != '': self.out.write('%s\n'%self.header) else: name = MARKUPDICT.get(NAME) self.out.write('
# %s
\ # %s

\n'%(name, self.title, time.ctime())) def _doCSSFooter(self): # Optional if self.footer != '': self.out.write('%s\n'%self.footer) else: self.out.write('
# %s
\ # %s
\n'%(MARKUPDICT.get(NAME),self.title, time.ctime())) def _doCSSEnd(self): # End of css/html page self.out.write(self.colors.get(CODEEND,'
\n')) # Write a little info at the bottom self._doPageFooter() self.out.write('\n') return ################################################## XHTML markup functions def _doXHTMLStart(self): # XHTML is really just XML + HTML 4.01. # We only need to change the page headers, # and a few tags to get valid XHTML. # Start of xhtml page self.out.write('\n \ \n \ \n') self.out.write('%s\n'%(self.title)) self.out.write(self._getDocumentCreatedBy()) self.out.write('\n') self._doXHTMLStyleSheet() self.out.write('\n\n') # Write a little info at the top. self._doPageHeader() self.out.write(self.colors.get(CODESTART,'
\n'))
        return

    def _doXHTMLStyleSheet(self):
        if not self.external:
            # write an embedded style sheet
            self.out.write(self._sendCSSStyle())
        else:
            # write a link to an external style sheet
            self.out.write('\n')
        return

    def _sendXHTMLText(self, toktype, toktext):
        self._sendCSSText(toktype, toktext)

    def _doXHTMLHeader(self):
        # Optional
        if self.header:
            self.out.write('%s\n'%self.header)
        else:
            name = MARKUPDICT.get(NAME)
            self.out.write('
# %s
\ # %s

\n '%( name, self.title, time.ctime())) def _doXHTMLFooter(self): # Optional if self.footer: self.out.write('%s\n'%self.footer) else: self.out.write('
# %s
\ # %s
\n'%(MARKUPDICT.get(NAME), self.title, time.ctime())) def _doXHTMLEnd(self): self._doCSSEnd() ############################################################################# if __name__ == '__main__': cli() ############################################################################# # PySourceColor.py # 2004, 2005 M.E.Farmer Jr. # Python license